package com.simulator;

/**
 * Polytech' Nice - Sophia Antipolis / SI3 / POO2
 * Annee 2011_2012
 * 
 * OMG Zombies !
 * 
 * Classe Simulator : Simulation du jeu
 * 
 * @author J. Harnetiaux et J. Lionne
 */

import java.awt.Color;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import fr.zombieGame.*;

public class Simulator {

	private static final double PROBABILITE_CREATION_HUMAIN = 0.1;
	private static final double PROBABILITE_CREATION_ZOMBIE = 0.08;
	private static final double PROBABILITE_CREATION_ZOMBIE_FOU = 0.02;
	private static final double PROBABILITE_CREATION_VAMPIRE = 0.02;

	private static final int POINTS_DE_VIE_HUMAIN = 150;
	private static final int POINTS_DE_VIE_ZOMBIE = 150;
	private static final int POINTS_DE_VIE_ZOMBIE_FOU = 250;
	private static final int POINTS_DE_VIE_VAMPIRE = 250;
	private static final int POINTS_DE_VIE_HELICOPTERE = 0;

	private static final int HAUTEUR_DEFAUT = 50;
	private static final int LARGEUR_DEFAUT = 50;

	private List<Humain> humains;
	private List<Zombie> zombies;
	private List<ZombieFou> zombiesFous;
	private List<Vampire> vampires;
	private List<Helicoptere> helicopteres;
	private Field field;
	private int step;
	private SimulatorView view;

	/**
	 * Construct a simulation field with default size.
	 */
	public Simulator() {
		this(HAUTEUR_DEFAUT, LARGEUR_DEFAUT);
	}

	/**
	 * Create a simulation field with the given size.
	 * 
	 * @param depth
	 *            Depth of the field. Must be greater than zero.
	 * @param width
	 *            Width of the field. Must be greater than zero.
	 */
	public Simulator(int depth, int width) {
		if (width <= 0 || depth <= 0) {
			System.out.println("The dimensions must be greater than zero.");
			System.out.println("Using default values.");
			depth = HAUTEUR_DEFAUT;
			width = LARGEUR_DEFAUT;
		}

		humains = new ArrayList<Humain>();
		zombies = new ArrayList<Zombie>();
		zombiesFous = new ArrayList<ZombieFou>();
		vampires = new ArrayList<Vampire>();
		helicopteres = new ArrayList<Helicoptere>();
		field = new Field(depth, width);

		// Create a view of the state of each location in the field.
		view = new SimulatorView(depth, width);
		view.setColor(Humain.class, Color.blue);
		view.setColor(Zombie.class, Color.orange);
		view.setColor(ZombieFou.class, Color.pink);
		view.setColor(Vampire.class, Color.red);
		view.setColor(Helicoptere.class, Color.green);

		// Setup a valid starting point.
		reset();
	}

	/**
	 * Run the simulation from its current state for a reasonably long period,
	 * e.g. 500 steps.
	 */
	public void runLongSimulation() {
		simulate(500);
	}

	/**
	 * Run the simulation from its current state for the given number of steps.
	 * Stop before the given number of steps if it ceases to be viable.
	 * 
	 * @param numSteps
	 *            The number of steps to run for.
	 */
	public void simulate(int numSteps) {
		for (int step = 1; step <= numSteps && view.isViable(field); step++) {
			simulateOneStep();
		}
	}

	/**
	 * Run the simulation from its current state for a single step. Iterate over
	 * the whole field updating the state of each fox and rabbit.
	 */
	public void simulateOneStep() {
		step++;

		// Provide space for newborn humans.
		List<Humain> newHumans = new ArrayList<Humain>();
		// Let all humans act.
		for (Iterator<Humain> it = humains.iterator(); it.hasNext();) {
			Humain human = it.next();
			human.jouer(newHumans);
			if (!human.estVivant()) {
				it.remove();
			}
		}

		// Let all zombies act.
		for (Iterator<Zombie> it = zombies.iterator(); it.hasNext();) {
			Zombie zombie = it.next();
			zombie.jouer();
			if (!zombie.estVivant()) {
				it.remove();
			}
		}

		// Let all mad zombies act.
		for (Iterator<ZombieFou> it = zombiesFous.iterator(); it.hasNext();) {
			ZombieFou zombieFou = it.next();
			zombieFou.jouer();
			if (!zombieFou.estVivant()) {
				it.remove();
			}
		}

		// Let all vampires act.
		for (Iterator<Vampire> it = vampires.iterator(); it.hasNext();) {
			Vampire vampire = it.next();
			vampire.jouer();
			if (!vampire.estVivant()) {
				it.remove();
			}
		}

		// Let all helicopter act.
		for (Iterator<Helicoptere> it = helicopteres.iterator(); it.hasNext();) {
			Helicoptere helico = it.next();
			helico.jouer();
		}

		// Add the newly born humains to the main lists.
		humains.addAll(newHumans);

		// Turn the bitten humans into vampire
		// Turn the dead humans futur zombie into zombie
		// Turn some zombies into mad zombies
		for(int row = 0; row < field.getDepth(); row++) {
			for(int col = 0; col < field.getWidth(); col++) {
				Object character = field.getObjectAt(row, col);
				if(character instanceof Humain) {
					if(((Humain) character).isMordu()) {
						Vampire vampire = ((Humain)character).transformerEnVampire();
						vampires.add(vampire);
						field.place(vampire, row, col);
					}
					else if(((Humain)character).isFuturZombie()) {
						Zombie zombie = ((Humain)character).transformerEnZombie();
						zombies.add(zombie);
						field.place(zombie, row, col);
					}

				} else if(character instanceof Zombie && ((Zombie)character).isFuturZombieFou()) {
					ZombieFou zombieFou = ((Zombie)character).transformerEnZombieFou();
					zombiesFous.add(zombieFou);
					field.place(zombieFou, row, col);
				}
			}
		}

		view.showStatus(step, field);
		try {
			Thread.sleep(150);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Reset the simulation to a starting position.
	 */
	public void reset() {
		step = 0;
		humains.clear();
		zombies.clear();
		vampires.clear();
		populate();

		// Show the starting state in the view.
		view.showStatus(step, field);
	}

	/**
	 * Randomly populate the field with foxes and rabbits.
	 */
	private void populate() {
		Random rand = Randomizer.getRandom();
		field.clear();
		for (int row = 0; row < field.getDepth(); row++) {
			for (int col = 0; col < field.getWidth(); col++) {
				if (rand.nextDouble() <= PROBABILITE_CREATION_HUMAIN) {
					Location location = new Location(row, col);
					Humain humain = new Humain("Humain" + row +"-"+ col, POINTS_DE_VIE_HUMAIN, field, location, true);
					humains.add(humain);
				} else if (rand.nextDouble() <= PROBABILITE_CREATION_VAMPIRE) {
					Location location = new Location(row, col);
					Vampire vampire = new Vampire("Vampire" + row +"-"+ col, POINTS_DE_VIE_VAMPIRE, field, location);
					vampires.add(vampire);
				} else if (rand.nextDouble() <= PROBABILITE_CREATION_ZOMBIE) {
					Location location = new Location(row, col);
					Zombie zombie = new Zombie("Zombie" + row +"-"+ col, POINTS_DE_VIE_ZOMBIE, field, location);
					zombies.add(zombie);
				}  else if (rand.nextDouble() <= PROBABILITE_CREATION_ZOMBIE_FOU) {
					Location location = new Location(row, col);
					ZombieFou zombieFou = new ZombieFou("ZombieFou" + row +"-"+ col, POINTS_DE_VIE_ZOMBIE_FOU, field, location);
					zombiesFous.add(zombieFou);
				}
			}
		}
		Location locationHelico = field.freeAdjacentLocation(new Location(0,0));
		Helicoptere helicoptere = new Helicoptere("Helicoptere",POINTS_DE_VIE_HELICOPTERE,field,locationHelico);
		helicopteres.add(helicoptere);
	}
	
		

}
